// Copyright (c) 2004-2011 Sergey Lyubka
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#if defined(_WIN32)
#define _CRT_SECURE_NO_WARNINGS	// Disable deprecation warning in VS2005
#else
#define _XOPEN_SOURCE 600	// For flockfile() on Linux
#define _LARGEFILE_SOURCE	// Enable 64-bit file offsets
#define __STDC_FORMAT_MACROS	// <inttypes.h> wants this for C++
#endif

#if defined(__SYMBIAN32__)
#define NO_SSL			// SSL is not supported
#define NO_CGI			// CGI is not supported
#define PATH_MAX FILENAME_MAX
#endif				// __SYMBIAN32__

#ifndef _WIN32_WCE		// Some ANSI #includes are not available on Windows CE
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#endif				// !_WIN32_WCE

#include <time.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stddef.h>
#include <stdio.h>

#if defined(_WIN32) && !defined(__SYMBIAN32__)	// Windows specific
#define _WIN32_WINNT 0x0400	// To make it link in VS2005
#include <windows.h>

#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif

#ifndef _WIN32_WCE
#include <process.h>
#include <direct.h>
#include <io.h>
#else				// _WIN32_WCE
#include <winsock2.h>
#define NO_CGI			// WinCE has no pipes


typedef long off_t;
#define BUFSIZ  4096

#define errno   GetLastError()
#define strerror(x)  _ultoa(x, (char *) _alloca(sizeof(x) *3 ), 10)
#endif				// _WIN32_WCE

#define MAKEUQUAD(lo, hi) ((uint64_t)(((uint32_t)(lo)) | \
      ((uint64_t)((uint32_t)(hi))) << 32))
#define RATE_DIFF 10000000	// 100 nsecs
#define EPOCH_DIFF MAKEUQUAD(0xd53e8000, 0x019db1de)
#define SYS2UNIX_TIME(lo, hi) \
  (time_t) ((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)


#define ERRNO   GetLastError()
#define NO_SOCKLEN_T
#define DIRSEP '\\'
#define IS_DIRSEP_CHAR(c) ((c) == '/' || (c) == '\\')
#define O_NONBLOCK  0
#if !defined(EWOULDBLOCK)
#define EWOULDBLOCK  WSAEWOULDBLOCK
#endif				// !EWOULDBLOCK
#define _POSIX_
#define INT64_FMT  "I64d"

#define WINCDECL __cdecl
#define SHUT_WR 1
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define sleep(x) Sleep((x) * 1000)

#define pipe(x) _pipe(x, BUFSIZ, _O_BINARY)
#define popen(x, y) _popen(x, y)
#define pclose(x) _pclose(x)
#define close(x) _close(x)
#define dlsym(x,y) GetProcAddress((HINSTANCE) (x), (y))
#define RTLD_LAZY  0
#define fseeko(x, y, z) fseek((x), (y), (z))
#define fdopen(x, y) _fdopen((x), (y))
#define write(x, y, z) _write((x), (y), (unsigned) z)
#define read(x, y, z) _read((x), (y), (unsigned) z)
#define flockfile(x) (void) 0
#define funlockfile(x) (void) 0

#if !defined(fileno)
#define fileno(x) _fileno(x)
#endif				// !fileno MINGW #defines fileno

typedef HANDLE pthread_mutex_t;
typedef struct {
	HANDLE signal, broadcast;
} pthread_cond_t;
typedef DWORD pthread_t;
#define pid_t HANDLE		// MINGW typedefs pid_t to int. Using #define here.

struct timespec {
	long tv_nsec;
	long tv_sec;
};

static int pthread_mutex_lock(pthread_mutex_t *);
static int pthread_mutex_unlock(pthread_mutex_t *);
static FILE *mg_fopen(const char *path, const char *mode);

#if defined(HAVE_STDINT)
#include <stdint.h>
#else
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned __int64 uint64_t;
typedef __int64 int64_t;
#define INT64_MAX  9223372036854775807
#endif				// HAVE_STDINT

// POSIX dirent interface
struct dirent {
	char d_name[PATH_MAX];
};

typedef struct DIR {
	HANDLE handle;
	WIN32_FIND_DATAW info;
	struct dirent result;
} DIR;

#else				// UNIX  specific
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <stdint.h>
#include <inttypes.h>
#include <netdb.h>

#include <pwd.h>
#include <unistd.h>
#include <dirent.h>
#include <pthread.h>
#define DIRSEP   '/'
#define IS_DIRSEP_CHAR(c) ((c) == '/')
#ifndef O_BINARY
#define O_BINARY  0
#endif				// O_BINARY
#define closesocket(a) close(a)
#define mg_fopen(x, y) fopen(x, y)
#define ERRNO errno
#define INVALID_SOCKET (-1)
#define INT64_FMT PRId64
typedef int SOCKET;
#define WINCDECL

#endif				// End of Windows and UNIX specific includes

#include "mongoose.h"

#define MONGOOSE_VERSION "3.1"
#define PASSWORDS_FILE_NAME ".htpasswd"
#define CGI_ENVIRONMENT_SIZE 4096
#define MAX_CGI_ENVIR_VARS 64
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))

#ifdef _WIN32
static pthread_t pthread_self(void)
{
	return GetCurrentThreadId();
}
#endif				// _WIN32

#if defined(DEBUG)
#define DEBUG_TRACE(x) do { \
  flockfile(stdout); \
  printf("*** %lu.%p.%s.%d: ", \
         (unsigned long) time(NULL), (void *) pthread_self(), \
         __func__, __LINE__); \
  printf x; \
  putchar('\n'); \
  fflush(stdout); \
  funlockfile(stdout); \
} while (0)
#else
#define DEBUG_TRACE(x)
#endif				// DEBUG

// Darwin prior to 7.0 and Win32 do not have socklen_t
#ifdef NO_SOCKLEN_T
typedef int socklen_t;
#endif				// NO_SOCKLEN_T

typedef void *(*mg_thread_func_t) (void *);

static const char *http_500_error = "Internal Server Error";

// Unified socket address. For IPv6 support, add IPv6 address structure
// in the union u.
union usa {
	struct sockaddr sa;
	struct sockaddr_in sin;
#if defined(USE_IPV6)
	struct sockaddr_in6 sin6;
#endif
};

// Describes a string (chunk of memory).
struct vec {
	const char *ptr;
	size_t len;
};

// Structure used by mg_stat() function. Uses 64 bit file length.
struct mgstat {
	int is_directory;	// Directory marker
	int64_t size;		// File size
	time_t mtime;		// Modification time
};

// Describes listening socket, or socket which was accept()-ed by the master
// thread and queued for future handling by the worker thread.
struct socket {
	struct socket *next;	// Linkage
	SOCKET sock;		// Listening socket
	union usa lsa;		// Local socket address
	union usa rsa;		// Remote socket address
};

enum {
	ACCESS_LOG_FILE,
	ERROR_LOG_FILE,
	ENABLE_KEEP_ALIVE,
	MAX_REQUEST_SIZE,
	EXTRA_MIME_TYPES,
	LISTENING_PORTS,
	NUM_THREADS,
	RUN_AS_USER,
	NUM_OPTIONS
};

static const char *config_options[] = {
	"a", "access_log_file", NULL,
	"e", "error_log_file", NULL,
	"k", "enable_keep_alive", "no",
	"M", "max_request_size", "16384",
	"m", "extra_mime_types", NULL,
	"p", "listening_ports", "8080",
	"t", "num_threads", "10",
	"u", "run_as_user", NULL,
	NULL
};

#define ENTRIES_PER_CONFIG_OPTION 3

struct mg_context {
	volatile int stop_flag;	// Should we stop event loop
	char *config[NUM_OPTIONS];	// Mongoose configuration parameters
	mg_callback_t user_callback;	// User-defined callback function
	void *user_data;	// User-defined data

	struct socket *listening_sockets;

	volatile int num_threads;	// Number of threads
	pthread_mutex_t mutex;	// Protects (max|num)_threads
	pthread_cond_t cond;	// Condvar for tracking workers terminations

	struct socket queue[20];	// Accepted sockets
	volatile int sq_head;	// Head of the socket queue
	volatile int sq_tail;	// Tail of the socket queue
	pthread_cond_t sq_full;	// Singaled when socket is produced
	pthread_cond_t sq_empty;	// Signaled when socket is consumed
};

struct mg_connection {
	struct mg_request_info request_info;
	struct mg_context *ctx;
	struct socket client;	// Connected client
	time_t birth_time;	// Time connection was accepted
	int64_t num_bytes_sent;	// Total bytes sent to client
	int64_t content_len;	// Content-Length header value
	int64_t consumed_content;	// How many bytes of content is already read
	char *buf;		// Buffer for received data
	int buf_size;		// Buffer size
	int request_len;	// Size of the request + headers in a buffer
	int data_len;		// Total size of data in a buffer
};

const char **mg_get_valid_option_names(void)
{
	return config_options;
}

static int call_user(struct mg_connection *conn, enum mg_event event)
{
	conn->request_info.user_data = conn->ctx->user_data;
	return conn->ctx->user_callback == NULL ? -1 :
	    conn->ctx->user_callback(event, conn, &conn->request_info);
}

static int get_option_index(const char *name)
{
	int i;

	for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_CONFIG_OPTION) {
		if (strcmp(config_options[i], name) == 0 ||
		    strcmp(config_options[i + 1], name) == 0) {
			return i / ENTRIES_PER_CONFIG_OPTION;
		}
	}
	return -1;
}

const char *mg_get_option(const struct mg_context *ctx, const char *name)
{
	int i;
	if ((i = get_option_index(name)) == -1) {
		return NULL;
	} else if (ctx->config[i] == NULL) {
		return "";
	} else {
		return ctx->config[i];
	}
}

static void sockaddr_to_string(char *buf, size_t len, const union usa *usa)
{
	buf[0] = '\0';
#if defined(USE_IPV6)
	inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
		  (void *)&usa->sin.sin_addr :
		  (void *)&usa->sin6.sin6_addr, buf, len);
#elif defined(_WIN32)
	// Only Windoze Vista (and newer) have inet_ntop()
	strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
#else
	inet_ntop(usa->sa.sa_family, (void *)&usa->sin.sin_addr, buf, len);
#endif
}

// Print error message to the opened error log stream.
static void cry(struct mg_connection *conn, const char *fmt, ...)
{
	char buf[BUFSIZ], src_addr[20];
	va_list ap;
	FILE *fp;
	time_t timestamp;

	va_start(ap, fmt);
	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
	va_end(ap);

	// Do not lock when getting the callback value, here and below.
	// I suppose this is fine, since function cannot disappear in the
	// same way string option can.
	conn->request_info.log_message = buf;
	if (call_user(conn, MG_EVENT_LOG) == -1) {
		fp = conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
		    mg_fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");

		if (fp != NULL) {
			flockfile(fp);
			timestamp = time(NULL);

			sockaddr_to_string(src_addr, sizeof(src_addr),
					   &conn->client.rsa);
			fprintf(fp, "[%010lu] [error] [client %s] ",
				(unsigned long)timestamp, src_addr);

			if (conn->request_info.request_method != NULL) {
				fprintf(fp, "%s %s: ",
					conn->request_info.request_method,
					conn->request_info.uri);
			}

			(void)fprintf(fp, "%s", buf);
			fputc('\n', fp);
			funlockfile(fp);
			if (fp != stderr) {
				fclose(fp);
			}
		}
	}
	conn->request_info.log_message = NULL;
}


// Return fake connection structure. Used for logging, if connection
// is not applicable at the moment of logging.
static struct mg_connection *fc(struct mg_context *ctx)
{
	static struct mg_connection fake_connection;
	fake_connection.ctx = ctx;
	return &fake_connection;
}

const char *mg_version(void)
{
	return MONGOOSE_VERSION;
}

static void mg_strlcpy(register char *dst, register const char *src, size_t n)
{
	for (; *src != '\0' && n > 1; n--) {
		*dst++ = *src++;
	}
	*dst = '\0';
}

static int lowercase(const char *s)
{
	return tolower(*(const unsigned char *)s);
}

static int mg_strncasecmp(const char *s1, const char *s2, size_t len)
{
	int diff = 0;

	if (len > 0)
		do {
			diff = lowercase(s1++) - lowercase(s2++);
		} while (diff == 0 && s1[-1] != '\0' && --len > 0);

	return diff;
}

static int mg_strcasecmp(const char *s1, const char *s2)
{
	int diff;

	do {
		diff = lowercase(s1++) - lowercase(s2++);
	} while (diff == 0 && s1[-1] != '\0');

	return diff;
}

static char *mg_strndup(const char *ptr, size_t len)
{
	char *p;

	if ((p = (char *)malloc(len + 1)) != NULL) {
		mg_strlcpy(p, ptr, len + 1);
	}

	return p;
}

static char *mg_strdup(const char *str)
{
	return mg_strndup(str, strlen(str));
}

static int mg_vsnwprintf(struct mg_connection *conn, wchar_t *buf, size_t buflen,
			const wchar_t *fmt, va_list ap)
{
	int n;

	if (buflen == 0)
		return 0;

	n = _vsnwprintf(buf, buflen, fmt, ap);

	if (n < 0) {
		cry(conn, "vsnwprintf error");
		n = 0;
	} else if (n >= (int) buflen) {
		cry(conn, "truncating vsnwprintf buffer: [%.*s]",
		    n > 200 ? 200 : n, buf);
		n = (int)buflen - 1;
	}
	buf[n] = L'\0';

	return n;
}

// Like snprintf(), but never returns negative value, or the value
// that is larger than a supplied buffer.
// Thanks to Adam Zeldis to pointing snprintf()-caused vulnerability
// in his audit report.
static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
			const char *fmt, va_list ap)
{
	int n;

	if (buflen == 0)
		return 0;

	n = vsnprintf(buf, buflen, fmt, ap);

	if (n < 0) {
		cry(conn, "vsnprintf error");
		n = 0;
	} else if (n >= (int)buflen) {
		cry(conn, "truncating vsnprintf buffer: [%.*s]",
		    n > 200 ? 200 : n, buf);
		n = (int)buflen - 1;
	}
	buf[n] = '\0';

	return n;
}

static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
		       const char *fmt, ...)
{
	va_list ap;
	int n;

	va_start(ap, fmt);
	n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
	va_end(ap);

	return n;
}

// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
// Advance pointer to buffer to the next word. Return found 0-terminated word.
// Delimiters can be quoted with quotechar.
static char *skip_quoted(char **buf, const char *delimiters,
			 const char *whitespace, char quotechar)
{
	char *p, *begin_word, *end_word, *end_whitespace;

	begin_word = *buf;
	end_word = begin_word + strcspn(begin_word, delimiters);

	// Check for quotechar
	if (end_word > begin_word) {
		p = end_word - 1;
		while (*p == quotechar) {
			// If there is anything beyond end_word, copy it
			if (*end_word == '\0') {
				*p = '\0';
				break;
			} else {
				size_t end_off =
				    strcspn(end_word + 1, delimiters);
				memmove(p, end_word, end_off + 1);
				p += end_off;	// p must correspond to end_word - 1
				end_word += end_off + 1;
			}
		}
		for (p++; p < end_word; p++) {
			*p = '\0';
		}
	}

	if (*end_word == '\0') {
		*buf = end_word;
	} else {
		end_whitespace =
		    end_word + 1 + strspn(end_word + 1, whitespace);

		for (p = end_word; p < end_whitespace; p++) {
			*p = '\0';
		}

		*buf = end_whitespace;
	}

	return begin_word;
}

// Simplified version of skip_quoted without quote char
// and whitespace == delimiters
static char *skip(char **buf, const char *delimiters)
{
	return skip_quoted(buf, delimiters, delimiters, 0);
}

// Return HTTP header value, or NULL if not found.
static const char *get_header(const struct mg_request_info *ri,
			      const char *name)
{
	int i;

	for (i = 0; i < ri->num_headers; i++)
		if (!mg_strcasecmp(name, ri->http_headers[i].name))
			return ri->http_headers[i].value;

	return NULL;
}

const char *mg_get_header(const struct mg_connection *conn, const char *name)
{
	return get_header(&conn->request_info, name);
}

// A helper function for traversing comma separated list of values.
// It returns a list pointer shifted to the next value, of NULL if the end
// of the list found.
// Value is stored in val vector. If value has form "x=y", then eq_val
// vector is initialized to point to the "y" part, and val vector length
// is adjusted to point only to "x".
static const char *next_option(const char *list, struct vec *val,
			       struct vec *eq_val)
{
	if (list == NULL || *list == '\0') {
		// End of the list
		list = NULL;
	} else {
		val->ptr = list;
		if ((list = strchr(val->ptr, ',')) != NULL) {
			// Comma found. Store length and shift the list ptr
			val->len = list - val->ptr;
			list++;
		} else {
			// This value is the last one
			list = val->ptr + strlen(val->ptr);
			val->len = list - val->ptr;
		}

		if (eq_val != NULL) {
			// Value has form "x=y", adjust pointers and lengths
			// so that val points to "x", and eq_val points to "y".
			eq_val->len = 0;
			eq_val->ptr =
			    (const char *)memchr(val->ptr, '=', val->len);
			if (eq_val->ptr != NULL) {
				eq_val->ptr++;	// Skip over '=' character
				eq_val->len = val->ptr + val->len - eq_val->ptr;
				val->len = (eq_val->ptr - val->ptr) - 1;
			}
		}
	}

	return list;
}

static int match_prefix(const char *pattern, int pattern_len, const char *str)
{
	const char *or_str;
	int i, j, len, res;

	if ((or_str = memchr(pattern, '|', pattern_len)) != NULL) {
		res = match_prefix(pattern, or_str - pattern, str);
		return res > 0 ? res :
		    match_prefix(or_str + 1,
				 (pattern + pattern_len) - (or_str + 1), str);
	}

	i = j = 0;
	res = -1;
	for (; i < pattern_len; i++, j++) {
		if (pattern[i] == '?' && str[j] != '\0') {
			continue;
		} else if (pattern[i] == '$') {
			return str[j] == '\0' ? j : -1;
		} else if (pattern[i] == '*') {
			i++;
			if (pattern[i] == '*') {
				i++;
				len = strlen(str + j);
			} else {
				len = strcspn(str + j, "/");
			}
			if (i == pattern_len) {
				return j + len;
			}
			do {
				res =
				    match_prefix(pattern + i, pattern_len - i,
						 str + j + len);
			} while (res == -1 && len-- > 0);
			return res == -1 ? -1 : j + res + len;
		} else if (pattern[i] != str[j]) {
			return -1;
		}
	}
	return j;
}

// HTTP 1.1 assumes keep alive if "Connection:" header is not set
// This function must tolerate situations when connection info is not
// set up, for example if request parsing failed.
static int should_keep_alive(const struct mg_connection *conn)
{
	const char *http_version = conn->request_info.http_version;
	const char *header = mg_get_header(conn, "Connection");
	return (!mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") &&
		(header == NULL && http_version
		 && !strcmp(http_version, "1.1"))) || (header != NULL
						       && !mg_strcasecmp(header,
									 "keep-alive"));
}

static const char *suggest_connection_header(const struct mg_connection *conn)
{
	return should_keep_alive(conn) ? "keep-alive" : "close";
}

void send_http_error(struct mg_connection *conn, int status,
			    const char *reason, const char *fmt, ...)
{
	char buf[BUFSIZ];
	va_list ap;
	int len;

	conn->request_info.status_code = status;

	if (call_user(conn, MG_HTTP_ERROR) == -1) {
		buf[0] = '\0';
		len = 0;

		// Errors 1xx, 204 and 304 MUST NOT send a body
		if (status > 199 && status != 204 && status != 304) {
			len =
			    mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s",
					status, reason);
			cry(conn, "%s", buf);
			buf[len++] = '\n';

			va_start(ap, fmt);
			len +=
			    mg_vsnprintf(conn, buf + len, sizeof(buf) - len,
					 fmt, ap);
			va_end(ap);
		}
		DEBUG_TRACE(("[%s]", buf));

		mg_printf(conn, "HTTP/1.1 %d %s\r\n"
			  "Content-Type: text/plain\r\n"
			  "Content-Length: %d\r\n"
			  "Connection: %s\r\n\r\n", status, reason, len,
			  suggest_connection_header(conn));
		conn->num_bytes_sent += mg_printf(conn, "%s", buf);
	}
}

#if defined(_WIN32) && !defined(__SYMBIAN32__)
static int pthread_mutex_init(pthread_mutex_t * mutex, void *unused)
{
	unused = NULL;
	*mutex = CreateMutex(NULL, FALSE, NULL);
	return *mutex == NULL ? -1 : 0;
}

static int pthread_mutex_destroy(pthread_mutex_t * mutex)
{
	return CloseHandle(*mutex) == 0 ? -1 : 0;
}

static int pthread_mutex_lock(pthread_mutex_t * mutex)
{
	return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1;
}

static int pthread_mutex_unlock(pthread_mutex_t * mutex)
{
	return ReleaseMutex(*mutex) == 0 ? -1 : 0;
}

static int pthread_cond_init(pthread_cond_t * cv, const void *unused)
{
	unused = NULL;
	cv->signal = CreateEvent(NULL, FALSE, FALSE, NULL);
	cv->broadcast = CreateEvent(NULL, TRUE, FALSE, NULL);
	return cv->signal != NULL && cv->broadcast != NULL ? 0 : -1;
}

static int pthread_cond_wait(pthread_cond_t * cv, pthread_mutex_t * mutex)
{
	HANDLE handles[] = { cv->signal, cv->broadcast };
	ReleaseMutex(*mutex);
	WaitForMultipleObjects(2, handles, FALSE, INFINITE);
	return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1;
}

static int pthread_cond_signal(pthread_cond_t * cv)
{
	return SetEvent(cv->signal) == 0 ? -1 : 0;
}

static int pthread_cond_broadcast(pthread_cond_t * cv)
{
	// Implementation with PulseEvent() has race condition, see
	// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
	return PulseEvent(cv->broadcast) == 0 ? -1 : 0;
}

static int pthread_cond_destroy(pthread_cond_t * cv)
{
	return CloseHandle(cv->signal) && CloseHandle(cv->broadcast) ? 0 : -1;
}

// For Windows, change all slashes to backslashes in path names.
static void change_slashes_to_backslashes(char *path)
{
	int i;

	for (i = 0; path[i] != '\0'; i++) {
		if (path[i] == '/')
			path[i] = '\\';
		// i > 0 check is to preserve UNC paths, like \\server\file.txt
		if (path[i] == '\\' && i > 0)
			while (path[i + 1] == '\\' || path[i + 1] == '/')
				(void)memmove(path + i + 1,
					      path + i + 2,
					      strlen(path + i + 1));
	}
}

// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
// wbuf and wbuf_len is a target buffer and its length.
static void to_unicode(const char *path, wchar_t * wbuf, size_t wbuf_len)
{
	char buf[PATH_MAX], buf2[PATH_MAX], *p;

	mg_strlcpy(buf, path, sizeof(buf));
	change_slashes_to_backslashes(buf);

	// Point p to the end of the file name
	p = buf + strlen(buf) - 1;

	// Trim trailing backslash character
	while (p > buf && *p == '\\' && p[-1] != ':') {
		*p-- = '\0';
	}

	// Protect from CGI code disclosure.
	// This is very nasty hole. Windows happily opens files with
	// some garbage in the end of file name. So fopen("a.cgi    ", "r")
	// actually opens "a.cgi", and does not return an error!
	if (*p == 0x20 ||	// No space at the end
	    (*p == 0x2e && p > buf) ||	// No '.' but allow '.' as full path
	    *p == 0x2b ||	// No '+'
	    (*p & ~0x7f)) {	// And generally no non-ascii chars
		(void)fprintf(stderr, "Rejecting suspicious path: [%s]", buf);
		wbuf[0] = L'\0';
	} else {
		// Convert to Unicode and back. If doubly-converted string does not
		// match the original, something is fishy, reject.
		MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len);
		WideCharToMultiByte(CP_UTF8, 0, wbuf, (int)wbuf_len, buf2,
				    sizeof(buf2), NULL, NULL);
		if (strcmp(buf, buf2) != 0) {
			wbuf[0] = L'\0';
		}
	}
}

static FILE *mg_fopen(const char *path, const char *mode)
{
	wchar_t wbuf[PATH_MAX], wmode[20];

	to_unicode(path, wbuf, ARRAY_SIZE(wbuf));
	MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode));

	return _wfopen(wbuf, wmode);
}

static int mg_stat(const char *path, struct mgstat *stp)
{
	int ok = -1;		// Error
	wchar_t wbuf[PATH_MAX];
	WIN32_FILE_ATTRIBUTE_DATA info;

	to_unicode(path, wbuf, ARRAY_SIZE(wbuf));

	if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) {
		stp->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh);
		stp->mtime = SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime,
					   info.ftLastWriteTime.dwHighDateTime);
		stp->is_directory =
		    info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
		ok = 0;		// Success
	}

	return ok;
}

#define set_close_on_exec(fd)	// No FD_CLOEXEC on Windows

static int start_thread(struct mg_context *ctx, mg_thread_func_t f, void *p)
{
	return _beginthread((void (__cdecl *) (void *))f, 0,
			    p) == -1UL ? -1 : 0;
}


static int set_non_blocking_mode(SOCKET sock)
{
	unsigned long on = 1;
	return ioctlsocket(sock, FIONBIO, &on);
}

#else
static int mg_stat(const char *path, struct mgstat *stp)
{
	struct stat st;
	int ok;

	if (stat(path, &st) == 0) {
		ok = 0;
		stp->size = st.st_size;
		stp->mtime = st.st_mtime;
		stp->is_directory = S_ISDIR(st.st_mode);
	} else {
		ok = -1;
	}

	return ok;
}

static void set_close_on_exec(int fd)
{
	(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
}

static int start_thread(struct mg_context *ctx, mg_thread_func_t func,
			void *param)
{
	pthread_t thread_id;
	pthread_attr_t attr;
	int retval;

	(void)pthread_attr_init(&attr);
	(void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
	// TODO(lsm): figure out why mongoose dies on Linux if next line is enabled
	// (void) pthread_attr_setstacksize(&attr, sizeof(struct mg_connection) * 5);

	if ((retval = pthread_create(&thread_id, &attr, func, param)) != 0) {
		cry(fc(ctx), "%s: %s", __func__, strerror(retval));
	}

	return retval;
}


static int set_non_blocking_mode(SOCKET sock)
{
	int flags;

	flags = fcntl(sock, F_GETFL, 0);
	(void)fcntl(sock, F_SETFL, flags | O_NONBLOCK);

	return 0;
}
#endif				// _WIN32

// Write data to the IO channel - opened file descriptor, socket or SSL
// descriptor. Return number of bytes written.
static int64_t push(FILE * fp, SOCKET sock, const char *buf,
		    int64_t len)
{
	int64_t sent;
	int n, k;

	sent = 0;
	while (sent < len) {

		// How many bytes we send in this iteration
		k = len - sent > INT_MAX ? INT_MAX : (int)(len - sent);

		if (fp != NULL) {
			n = fwrite(buf + sent, 1, (size_t) k, fp);
			if (ferror(fp))
				n = -1;
		} else {
			n = send(sock, buf + sent, (size_t) k, 0);
		}

		if (n < 0)
			break;

		sent += n;
	}

	return sent;
}

// Read from IO channel - opened file descriptor, socket, or SSL descriptor.
// Return number of bytes read.
static int pull(FILE * fp, SOCKET sock, char *buf, int len)
{
	int nread;

	if (fp != NULL) {
		// Use read() instead of fread(), because if we're reading from the CGI
		// pipe, fread() may block until IO buffer is filled up. We cannot afford
		// to block and must pass all read bytes immediately to the client.
		nread = read(fileno(fp), buf, (size_t) len);
		if (ferror(fp))
			nread = -1;
	} else {
		nread = recv(sock, buf, (size_t) len, 0);
	}

	return nread;
}

int mg_read(struct mg_connection *conn, void *buf, size_t len)
{
	int n, buffered_len, nread;
	const char *buffered;

	assert((conn->content_len == -1 && conn->consumed_content == 0) ||
	       conn->consumed_content <= conn->content_len);
	DEBUG_TRACE(("%p %zu %lld %lld", buf, len,
		     conn->content_len, conn->consumed_content));
	nread = 0;
	if (conn->consumed_content < conn->content_len) {

		// Adjust number of bytes to read.
		int64_t to_read = conn->content_len - conn->consumed_content;
		if (to_read < (int64_t) len) {
			len = (int)to_read;
		}
		// How many bytes of data we have buffered in the request buffer?
		buffered =
		    conn->buf + conn->request_len + conn->consumed_content;
		buffered_len = conn->data_len - conn->request_len;
		assert(buffered_len >= 0);

		// Return buffered data back if we haven't done that yet.
		if (conn->consumed_content < (int64_t) buffered_len) {
			buffered_len -= (int)conn->consumed_content;
			if (len < (size_t) buffered_len) {
				buffered_len = len;
			}
			memcpy(buf, buffered, (size_t) buffered_len);
			len -= buffered_len;
			buf = (char *)buf + buffered_len;
			conn->consumed_content += buffered_len;
			nread = buffered_len;
		}
		// We have returned all buffered data. Read new data from the remote socket.
		while (len > 0) {
			n = pull(NULL, conn->client.sock,
				 (char *)buf, (int)len);
			if (n <= 0) {
				break;
			}
			buf = (char *)buf + n;
			conn->consumed_content += n;
			nread += n;
			len -= n;
		}
	}
	return nread;
}

int mg_wprintf(struct mg_connection *conn, const wchar_t *fmt, ...)
{
#if 0
	char buf[BUFSIZ];
	int len;

	len = wcslen(wcs);
	if (len <= 0)
		return 0;
	len = WideCharToMultiByte(CP_UTF8, 0, wcs, len, buf, sizeof(buf), NULL, NULL);
	if (len > 0)
		return mg_write(conn, buf, len);
	return 0;
#endif
	wchar_t wbuf[BUFSIZ];
	char buf[BUFSIZ];
	int len;
	va_list ap;

	va_start(ap, fmt);
	len = mg_vsnwprintf(conn, wbuf, sizeof(buf), fmt, ap);
	va_end(ap);

	len = WideCharToMultiByte(CP_UTF8, 0, wbuf, len, buf, sizeof(buf), NULL, NULL);

	return mg_write(conn, buf, (size_t) len);
}

int mg_write(struct mg_connection *conn, const void *buf, size_t len)
{
	return (int)push(NULL, conn->client.sock, (const char *)buf,
			 (int64_t) len);
}

int mg_printf(struct mg_connection *conn, const char *fmt, ...)
{
	char buf[BUFSIZ];
	int len;
	va_list ap;

	va_start(ap, fmt);
	len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap);
	va_end(ap);

	return mg_write(conn, buf, (size_t) len);
}

// URL-decode input buffer into destination buffer.
// 0-terminate the destination buffer. Return the length of decoded data.
// form-url-encoded data differs from URI encoding in a way that it
// uses '+' as character for space, see RFC 1866 section 8.2.1
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
static size_t url_decode(const char *src, size_t src_len, char *dst,
			 size_t dst_len, int is_form_url_encoded)
{
	size_t i, j;
	int a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')

	for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
		if (src[i] == '%' &&
		    isxdigit(*(const unsigned char *)(src + i + 1)) &&
		    isxdigit(*(const unsigned char *)(src + i + 2))) {
			a = tolower(*(const unsigned char *)(src + i + 1));
			b = tolower(*(const unsigned char *)(src + i + 2));
			dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b));
			i += 2;
		} else if (is_form_url_encoded && src[i] == '+') {
			dst[j] = ' ';
		} else {
			dst[j] = src[i];
		}
	}

	dst[j] = '\0';		// Null-terminate the destination

	return j;
}

// Scan given buffer and fetch the value of the given variable.
// It can be specified in query string, or in the POST data.
// Return NULL if the variable not found, or allocated 0-terminated value.
// It is caller's responsibility to free the returned value.
int mg_get_var(const char *buf, size_t buf_len, const char *name,
	       char *dst, size_t dst_len)
{
	const char *p, *e, *s;
	size_t name_len, len;

	name_len = strlen(name);
	e = buf + buf_len;
	len = -1;
	dst[0] = '\0';

	// buf is "var1=val1&var2=val2...". Find variable first
	for (p = buf; p != NULL && p + name_len < e; p++) {
		if ((p == buf || p[-1] == '&') && p[name_len] == '=' &&
		    !mg_strncasecmp(name, p, name_len)) {

			// Point p to variable value
			p += name_len + 1;

			// Point s to the end of the value
			s = (const char *)memchr(p, '&', (size_t) (e - p));
			if (s == NULL) {
				s = e;
			}
			assert(s >= p);

			// Decode variable into destination buffer
			if ((size_t) (s - p) < dst_len) {
				len =
				    url_decode(p, (size_t) (s - p), dst,
					       dst_len, 1);
			}
			break;
		}
	}

	return len;
}

int mg_get_cookie(const struct mg_connection *conn, const char *cookie_name,
		  char *dst, size_t dst_size)
{
	const char *s, *p, *end;
	int name_len, len = -1;

	dst[0] = '\0';
	if ((s = mg_get_header(conn, "Cookie")) == NULL) {
		return 0;
	}

	name_len = strlen(cookie_name);
	end = s + strlen(s);

	for (; (s = strstr(s, cookie_name)) != NULL; s += name_len)
		if (s[name_len] == '=') {
			s += name_len + 1;
			if ((p = strchr(s, ' ')) == NULL)
				p = end;
			if (p[-1] == ';')
				p--;
			if (*s == '"' && p[-1] == '"' && p > s + 1) {
				s++;
				p--;
			}
			if ((size_t) (p - s) < dst_size) {
				len = (p - s) + 1;
				mg_strlcpy(dst, s, (size_t) len);
			}
			break;
		}

	return len;
}


static struct mg_connection *mg_connect(struct mg_connection *conn,
					const char *host, int port)
{
	struct mg_connection *newconn = NULL;
	struct sockaddr_in sin;
	struct hostent *he;
	int sock;

	if ((he = gethostbyname(host)) == NULL) {
		cry(conn, "%s: gethostbyname(%s): %s", __func__, host,
		    strerror(ERRNO));
	} else if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
		cry(conn, "%s: socket: %s", __func__, strerror(ERRNO));
	} else {
		sin.sin_family = AF_INET;
		sin.sin_port = htons((uint16_t) port);
		sin.sin_addr = *(struct in_addr *)he->h_addr_list[0];
		if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
			cry(conn, "%s: connect(%s:%d): %s", __func__, host,
			    port, strerror(ERRNO));
			closesocket(sock);
		} else if ((newconn = (struct mg_connection *)
			    calloc(1, sizeof(*newconn))) == NULL) {
			cry(conn, "%s: calloc: %s", __func__, strerror(ERRNO));
			closesocket(sock);
		} else {
			newconn->client.sock = sock;
			newconn->client.rsa.sin = sin;
		}
	}

	return newconn;
}

// Check whether full request is buffered. Return:
//   -1  if request is malformed
//    0  if request is not yet fully buffered
//   >0  actual request length, including last \r\n\r\n
static int get_request_len(const char *buf, int buflen)
{
	const char *s, *e;
	int len = 0;

	DEBUG_TRACE(("buf: %p, len: %d", buf, buflen));
	for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
		// Control characters are not allowed but >=128 is.
		if (!isprint(*(const unsigned char *)s) && *s != '\r' &&
		    *s != '\n' && *(const unsigned char *)s < 128) {
			len = -1;
		} else if (s[0] == '\n' && s[1] == '\n') {
			len = (int)(s - buf) + 2;
		} else if (s[0] == '\n' && &s[1] < e &&
			   s[1] == '\r' && s[2] == '\n') {
			len = (int)(s - buf) + 3;
		}

	return len;
}

static const char *month_names[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};


// Convert month to the month number. Return -1 on error, or month number
static int get_month_index(const char *s)
{
	size_t i;

	for (i = 0; i < ARRAY_SIZE(month_names); i++)
		if (!strcmp(s, month_names[i]))
			return (int) i;

	return -1;
}

// Parse UTC date-time string, and return the corresponding time_t value.
static time_t parse_date_string(const char *datetime)
{
	static const unsigned short days_before_month[] = {
		0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
	};
	char month_str[32];
	int second, minute, hour, day, month, year, leap_days, days;
	time_t result = (time_t) 0;

	if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
						&day, month_str, &year, &hour, &minute, &second) == 6) ||
				(sscanf(datetime, "%d %3s %d %d:%d:%d",
					&day, month_str, &year, &hour, &minute, &second) == 6) ||
				(sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
					&day, month_str, &year, &hour, &minute, &second) == 6) ||
				(sscanf(datetime, "%d-%3s-%d %d:%d:%d",
					&day, month_str, &year, &hour, &minute, &second) == 6)) &&
			year > 1970 &&
			(month = get_month_index(month_str)) != -1) {
		year -= 1970;
		leap_days = year / 4 - year / 100 + year / 400;
		days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
		result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
	}

	return result;
}


// Protect against directory disclosure attack by removing '..',
// excessive '/' and '\' characters
static void remove_double_dots_and_double_slashes(char *s)
{
	char *p = s;

	while (*s != '\0') {
		*p++ = *s++;
		if (s[-1] == '/' || s[-1] == '\\') {
			// Skip all following slashes and backslashes
			while (*s == '/' || *s == '\\') {
				s++;
			}

			// Skip all double-dots
			while (*s == '.' && s[1] == '.') {
				s += 2;
			}
		}
	}
	*p = '\0';
}

static const struct {
	const char *extension;
	size_t ext_len;
	const char *mime_type;
	size_t mime_type_len;
} builtin_mime_types[] = {
	{
	".html", 5, "text/html", 9}, {
	".htm", 4, "text/html", 9}, {
	".shtm", 5, "text/html", 9}, {
	".shtml", 6, "text/html", 9}, {
	".css", 4, "text/css", 8}, {
	".js", 3, "application/x-javascript", 24}, {
	".ico", 4, "image/x-icon", 12}, {
	".gif", 4, "image/gif", 9}, {
	".jpg", 4, "image/jpeg", 10}, {
	".jpeg", 5, "image/jpeg", 10}, {
	".png", 4, "image/png", 9}, {
	".svg", 4, "image/svg+xml", 13}, {
	".torrent", 8, "application/x-bittorrent", 24}, {
	".wav", 4, "audio/x-wav", 11}, {
	".mp3", 4, "audio/x-mp3", 11}, {
	".mid", 4, "audio/mid", 9}, {
	".m3u", 4, "audio/x-mpegurl", 15}, {
	".ram", 4, "audio/x-pn-realaudio", 20}, {
	".xml", 4, "text/xml", 8}, {
	".xslt", 5, "application/xml", 15}, {
	".ra", 3, "audio/x-pn-realaudio", 20}, {
	".doc", 4, "application/msword", 19}, {
	".exe", 4, "application/octet-stream", 24}, {
	".zip", 4, "application/x-zip-compressed", 28}, {
	".xls", 4, "application/excel", 17}, {
	".tgz", 4, "application/x-tar-gz", 20}, {
	".tar", 4, "application/x-tar", 17}, {
	".gz", 3, "application/x-gunzip", 20}, {
	".arj", 4, "application/x-arj-compressed", 28}, {
	".rar", 4, "application/x-arj-compressed", 28}, {
	".rtf", 4, "application/rtf", 15}, {
	".pdf", 4, "application/pdf", 15}, {
	".swf", 4, "application/x-shockwave-flash", 29}, {
	".mpg", 4, "video/mpeg", 10}, {
	".mpeg", 5, "video/mpeg", 10}, {
	".mp4", 4, "video/mp4", 9}, {
	".m4v", 4, "video/x-m4v", 11}, {
	".asf", 4, "video/x-ms-asf", 14}, {
	".avi", 4, "video/x-msvideo", 15}, {
	".bmp", 4, "image/bmp", 9}, {
	NULL, 0, NULL, 0}
};

// Look at the "path" extension and figure what mime type it has.
// Store mime type in the vector.
static void get_mime_type(struct mg_context *ctx, const char *path,
			  struct vec *vec)
{
	struct vec ext_vec, mime_vec;
	const char *list, *ext;
	size_t i, path_len;

	path_len = strlen(path);

	// Scan user-defined mime types first, in case user wants to
	// override default mime types.
	list = ctx->config[EXTRA_MIME_TYPES];
	while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
		// ext now points to the path suffix
		ext = path + path_len - ext_vec.len;
		if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
			*vec = mime_vec;
			return;
		}
	}

	// Now scan built-in mime types
	for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
		ext = path + (path_len - builtin_mime_types[i].ext_len);
		if (path_len > builtin_mime_types[i].ext_len &&
		    mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) {
			vec->ptr = builtin_mime_types[i].mime_type;
			vec->len = builtin_mime_types[i].mime_type_len;
			return;
		}
	}

	// Nothing found. Fall back to "text/plain"
	vec->ptr = "text/plain";
	vec->len = 10;
}


// Stringify binary data. Output buffer must be twice as big as input,
// because each byte takes 2 bytes in string representation
static void bin2str(char *to, const unsigned char *p, size_t len)
{
	static const char *hex = "0123456789abcdef";

	for (; len--; p++) {
		*to++ = hex[p[0] >> 4];
		*to++ = hex[p[0] & 0x0f];
	}
	*to = '\0';
}


static void url_encode(const char *src, char *dst, size_t dst_len)
{
	static const char *dont_escape = "._-$,;~()";
	static const char *hex = "0123456789abcdef";
	const char *end = dst + dst_len - 1;

	for (; *src != '\0' && dst < end; src++, dst++) {
		if (isalnum(*(const unsigned char *)src) ||
		    strchr(dont_escape, *(const unsigned char *)src) != NULL) {
			*dst = *src;
		} else if (dst + 2 < end) {
			dst[0] = '%';
			dst[1] = hex[(*(const unsigned char *)src) >> 4];
			dst[2] = hex[(*(const unsigned char *)src) & 0xf];
			dst += 2;
		}
	}

	*dst = '\0';
}

// Send len bytes from the opened file to the client.
static void send_file_data(struct mg_connection *conn, FILE *fp, int64_t len)
{
	char buf[BUFSIZ];
	int to_read, num_read, num_written;

	while (len > 0) {
		// Calculate how much to read from the file in the buffer
		to_read = sizeof(buf);
		if ((int64_t) to_read > len)
			to_read = (int) len;

		// Read from file, exit the loop on error
		if ((num_read = fread(buf, 1, (size_t)to_read, fp)) == 0)
			break;

		// Send read bytes to the client, exit the loop on error
		if ((num_written = mg_write(conn, buf, (size_t)num_read)) != num_read)
			break;

		// Both read and were successful, adjust counters
		conn->num_bytes_sent += num_written;
		len -= num_written;
	}
}


static void gmt_time_string(char *buf, size_t buf_len, time_t *t)
{
	strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
}

static void handle_file_request(struct mg_connection *conn, const char *path,
		struct mgstat *stp)
{
	char date[64], lm[64], etag[64], range[64];
	const char *msg = "OK";
	time_t curtime = time(NULL);
	int64_t cl;
	struct vec mime_vec;
	FILE *fp;
	int n;

	get_mime_type(conn->ctx, path, &mime_vec);
	cl = stp->size;
	conn->request_info.status_code = 200;
	range[0] = '\0';

	if ((fp = mg_fopen(path, "rb")) == NULL) {
		send_http_error(conn, 500, http_500_error,
				"fopen(%s): %s", path, strerror(ERRNO));
		return;
	}
	set_close_on_exec(fileno(fp));

	// Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
	// http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
	gmt_time_string(date, sizeof(date), &curtime);
	gmt_time_string(lm, sizeof(lm), &stp->mtime);
	(void) mg_snprintf(conn, etag, sizeof(etag), "%lx.%lx",
			(unsigned long) stp->mtime, (unsigned long) stp->size);

	(void) mg_printf(conn,
			"HTTP/1.1 %d %s\r\n"
			"Date: %s\r\n"
			"Last-Modified: %s\r\n"
			"Etag: \"%s\"\r\n"
			"Content-Type: %.*s\r\n"
			"Content-Length: %" INT64_FMT "\r\n"
			"Connection: %s\r\n"
			"Accept-Ranges: bytes\r\n"
			"%s\r\n",
			conn->request_info.status_code, msg, date, lm, etag, (int) mime_vec.len,
			mime_vec.ptr, cl, suggest_connection_header(conn), range);

	if (strcmp(conn->request_info.request_method, "HEAD") != 0) {
		send_file_data(conn, fp, cl);
	}
	(void) fclose(fp);
}

// Return True if we should reply 304 Not Modified.
static int is_not_modified(const struct mg_connection *conn,
                           const struct mgstat *stp)
{
	const char *ims = mg_get_header(conn, "If-Modified-Since");
	return ims != NULL && stp->mtime <= parse_date_string(ims);
}


void mg_send_file(struct mg_connection *conn, const char *path)
{
	struct mgstat st;

	if (!path)
		goto out;

	if (mg_stat(path, &st) != 0)
		goto out;

	if (is_not_modified(conn, &st))
		send_http_error(conn, 304, "Not Modified", "");
	else
		handle_file_request(conn, path, &st);

	return;

out:
	send_http_error(conn, 404, "Not Found", "%s", "File not found");
}


// Parse HTTP headers from the given buffer, advance buffer to the point
// where parsing stopped.
static void parse_http_headers(char **buf, struct mg_request_info *ri)
{
	int i;

	for (i = 0; i < (int)ARRAY_SIZE(ri->http_headers); i++) {
		ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
		ri->http_headers[i].value = skip(buf, "\r\n");
		if (ri->http_headers[i].name[0] == '\0')
			break;
		ri->num_headers = i + 1;
	}
}

static int is_valid_http_method(const char *method)
{
	return !strcmp(method, "GET") || !strcmp(method, "POST") ||
	    !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
	    !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
	    !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND");
}

// Parse HTTP request, fill in mg_request_info structure.
static int parse_http_request(char *buf, struct mg_request_info *ri)
{
	int status = 0;

	// RFC says that all initial whitespaces should be ingored
	while (*buf != '\0' && isspace(*(unsigned char *)buf)) {
		buf++;
	}

	ri->request_method = skip(&buf, " ");
	ri->uri = skip(&buf, " ");
	ri->http_version = skip(&buf, "\r\n");

	if (is_valid_http_method(ri->request_method) &&
	    strncmp(ri->http_version, "HTTP/", 5) == 0) {
		ri->http_version += 5;	// Skip "HTTP/"
		parse_http_headers(&buf, ri);
		status = 1;
	}

	return status;
}

// Keep reading the input (either opened file descriptor fd, or socket sock,
// or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the
// buffer (which marks the end of HTTP request). Buffer buf may already
// have some data. The length of the data is stored in nread.
// Upon every read operation, increase nread by the number of bytes read.
static int read_request(FILE * fp, SOCKET sock, char *buf,
			int bufsiz, int *nread)
{
	int n, request_len;

	request_len = 0;
	while (*nread < bufsiz && request_len == 0) {
		n = pull(fp, sock, buf + *nread, bufsiz - *nread);
		if (n <= 0) {
			break;
		} else {
			*nread += n;
			request_len = get_request_len(buf, *nread);
		}
	}

	return request_len;
}


// This is the heart of the Mongoose's logic.
// This function is called when the request is read, parsed and validated,
// and Mongoose must decide what action to take: serve a file, or
// a directory, or call embedded function, etcetera.
static void handle_request(struct mg_connection *conn)
{
	struct mg_request_info *ri = &conn->request_info;
	int uri_len;
	struct mgstat st;

	if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
		*conn->request_info.query_string++ = '\0';
	}
	uri_len = strlen(ri->uri);
	url_decode(ri->uri, (size_t) uri_len, ri->uri, (size_t) (uri_len + 1),
		   0);
	remove_double_dots_and_double_slashes(ri->uri);

	DEBUG_TRACE(("%s", ri->uri));
	if (call_user(conn, MG_NEW_REQUEST) == -1) {
		/* handle it with 404 */
		send_http_error(conn, 404, "Not Found", "%s", "File not found");
	}
}

static void close_all_listening_sockets(struct mg_context *ctx)
{
	struct socket *sp, *tmp;
	for (sp = ctx->listening_sockets; sp != NULL; sp = tmp) {
		tmp = sp->next;
		(void)closesocket(sp->sock);
		free(sp);
	}
}

// Valid listening port specification is: [ip_address:]port[s]
// Examples: 80, 443s, 127.0.0.1:3128,1.2.3.4:8080s
// TODO(lsm): add parsing of the IPv6 address
static int parse_port_string(const struct vec *vec, struct socket *so)
{
	int a, b, c, d, port, len;

	// MacOS needs that. If we do not zero it, subsequent bind() will fail.
	// Also, all-zeroes in the socket address means binding to all addresses
	// for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
	memset(so, 0, sizeof(*so));

	if (sscanf(vec->ptr, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &len) ==
	    5) {
		// Bind to a specific IPv4 address
		so->lsa.sin.sin_addr.s_addr =
		    htonl((a << 24) | (b << 16) | (c << 8) | d);
	} else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 || len <= 0
		   || len > (int)vec->len || (vec->ptr[len]
					      && vec->ptr[len] != 's'
					      && vec->ptr[len] != ',')) {
		return 0;
	}

#if defined(USE_IPV6)
	so->lsa.sin6.sin6_family = AF_INET6;
	so->lsa.sin6.sin6_port = htons((uint16_t) port);
#else
	so->lsa.sin.sin_family = AF_INET;
	so->lsa.sin.sin_port = htons((uint16_t) port);
#endif

	return 1;
}

static int set_ports_option(struct mg_context *ctx)
{
	const char *list = ctx->config[LISTENING_PORTS];
	int on = 1, success = 1;
	SOCKET sock;
	struct vec vec;
	struct socket so, *listener;

	while (success && (list = next_option(list, &vec, NULL)) != NULL) {
		if (!parse_port_string(&vec, &so)) {
			cry(fc(ctx),
			    "%s: %.*s: invalid port spec. Expecting list of: %s",
			    __func__, vec.len, vec.ptr,
			    "[IP_ADDRESS:]PORT[s|p]");
			success = 0;
		} else if ((sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6))
			   == INVALID_SOCKET ||
#if !defined(_WIN32)
			   // On Windows, SO_REUSEADDR is recommended only for
			   // broadcast UDP sockets
			   setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
				      sizeof(on)) != 0 ||
#endif				// !_WIN32
			   // Set TCP keep-alive. This is needed because if HTTP-level
			   // keep-alive is enabled, and client resets the connection,
			   // server won't get TCP FIN or RST and will keep the connection
			   // open forever. With TCP keep-alive, next keep-alive
			   // handshake will figure out that the client is down and
			   // will close the server end.
			   // Thanks to Igor Klopov who suggested the patch.
			   setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,
				      (void *)&on, sizeof(on)) != 0
			   || bind(sock, &so.lsa.sa, sizeof(so.lsa)) != 0
			   || listen(sock, 100) != 0) {
			closesocket(sock);
			cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
			    vec.len, vec.ptr, strerror(ERRNO));
			success = 0;
		} else if ((listener = (struct socket *)
			    calloc(1, sizeof(*listener))) == NULL) {
			closesocket(sock);
			cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));
			success = 0;
		} else {
			*listener = so;
			listener->sock = sock;
			set_close_on_exec(listener->sock);
			listener->next = ctx->listening_sockets;
			ctx->listening_sockets = listener;
		}
	}

	if (!success) {
		close_all_listening_sockets(ctx);
	}

	return success;
}

static void log_header(const struct mg_connection *conn, const char *header,
		       FILE * fp)
{
	const char *header_value;

	if ((header_value = mg_get_header(conn, header)) == NULL) {
		(void)fprintf(fp, "%s", " -");
	} else {
		(void)fprintf(fp, " \"%s\"", header_value);
	}
}

static void log_access(const struct mg_connection *conn)
{
	const struct mg_request_info *ri;
	FILE *fp;
	char date[64], src_addr[20];

	fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ? NULL :
	    mg_fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");

	if (fp == NULL)
		return;

	strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
		 localtime(&conn->birth_time));

	ri = &conn->request_info;
	flockfile(fp);

	sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa);
	fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
		src_addr, ri->remote_user == NULL ? "-" : ri->remote_user, date,
		ri->request_method ? ri->request_method : "-",
		ri->uri ? ri->uri : "-", ri->http_version,
		conn->request_info.status_code, conn->num_bytes_sent);
	log_header(conn, "Referer", fp);
	log_header(conn, "User-Agent", fp);
	fputc('\n', fp);
	fflush(fp);

	funlockfile(fp);
	fclose(fp);
}

static int isbyte(int n)
{
	return n >= 0 && n <= 255;
}

static void add_to_set(SOCKET fd, fd_set * set, int *max_fd)
{
	FD_SET(fd, set);
	if (fd > (SOCKET) * max_fd) {
		*max_fd = (int)fd;
	}
}

#if !defined(_WIN32)
static int set_uid_option(struct mg_context *ctx)
{
	struct passwd *pw;
	const char *uid = ctx->config[RUN_AS_USER];
	int success = 0;

	if (uid == NULL) {
		success = 1;
	} else {
		if ((pw = getpwnam(uid)) == NULL) {
			cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
		} else if (setgid(pw->pw_gid) == -1) {
			cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid,
			    strerror(errno));
		} else if (setuid(pw->pw_uid) == -1) {
			cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid,
			    strerror(errno));
		} else {
			success = 1;
		}
	}

	return success;
}
#endif				// !_WIN32


static void reset_per_request_attributes(struct mg_connection *conn)
{
	struct mg_request_info *ri = &conn->request_info;

	// Reset request info attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
	if (ri->remote_user != NULL) {
		free((void *)ri->remote_user);
	}
	ri->remote_user = ri->request_method = ri->uri = ri->http_version =
	    NULL;
	ri->num_headers = 0;
	ri->status_code = -1;

	conn->num_bytes_sent = conn->consumed_content = 0;
	conn->content_len = -1;
	conn->request_len = conn->data_len = 0;
}

static void close_socket_gracefully(SOCKET sock)
{
	char buf[BUFSIZ];
	struct linger linger;
	int n;

	// Set linger option to avoid socket hanging out after close. This prevent
	// ephemeral port exhaust problem under high QPS.
	linger.l_onoff = 1;
	linger.l_linger = 1;
	setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger,
		   sizeof(linger));

	// Send FIN to the client
	(void)shutdown(sock, SHUT_WR);
	set_non_blocking_mode(sock);

	// Read and discard pending data. If we do not do that and close the
	// socket, the data in the send buffer may be discarded. This
	// behaviour is seen on Windows, when client keeps sending data
	// when server decide to close the connection; then when client
	// does recv() it gets no data back.
	do {
		n = pull(NULL, sock, buf, sizeof(buf));
	} while (n > 0);

	// Now we know that our FIN is ACK-ed, safe to close
	(void)closesocket(sock);
}

static void close_connection(struct mg_connection *conn)
{
	if (conn->client.sock != INVALID_SOCKET) {
		close_socket_gracefully(conn->client.sock);
	}
}

static void discard_current_request_from_buffer(struct mg_connection *conn)
{
	char *buffered;
	int buffered_len, body_len;

	buffered = conn->buf + conn->request_len;
	buffered_len = conn->data_len - conn->request_len;
	assert(buffered_len >= 0);

	if (conn->content_len == -1) {
		body_len = 0;
	} else if (conn->content_len < (int64_t) buffered_len) {
		body_len = (int)conn->content_len;
	} else {
		body_len = buffered_len;
	}

	conn->data_len -= conn->request_len + body_len;
	memmove(conn->buf, conn->buf + conn->request_len + body_len,
		(size_t) conn->data_len);
}

static int parse_url(const char *url, char *host, int *port)
{
	int len;

	if (sscanf(url, "%*[htps]://%1024[^:]:%d%n", host, port, &len) == 2 ||
	    sscanf(url, "%1024[^:]:%d%n", host, port, &len) == 2) {
	} else if (sscanf(url, "%*[htps]://%1024[^/]%n", host, &len) == 1) {
		*port = 80;
	} else {
		sscanf(url, "%1024[^/]%n", host, &len);
		*port = 80;
	}
	DEBUG_TRACE(("Host:%s, port:%d", host, *port));

	return len;
}

static int is_valid_uri(const char *uri)
{
	// Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
	// URI can be an asterisk (*) or should start with slash.
	return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
}

static void process_new_connection(struct mg_connection *conn)
{
	struct mg_request_info *ri = &conn->request_info;
	int keep_alive_enabled;
	const char *cl;

	keep_alive_enabled =
	    !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");

	do {
		reset_per_request_attributes(conn);

		// If next request is not pipelined, read it in
		if ((conn->request_len =
		     get_request_len(conn->buf, conn->data_len)) == 0) {
			conn->request_len =
			    read_request(NULL, conn->client.sock,
					 conn->buf, conn->buf_size,
					 &conn->data_len);
		}
		assert(conn->data_len >= conn->request_len);
		if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
			send_http_error(conn, 413, "Request Too Large", "");
			return;
		}
		if (conn->request_len <= 0) {
			return;	// Remote end closed the connection
		}
		// Nul-terminate the request cause parse_http_request() uses sscanf
		conn->buf[conn->request_len - 1] = '\0';
		if (!parse_http_request(conn->buf, ri) ||
				!is_valid_uri(ri->uri)) {
			// Do not put garbage in the access log, just send it back to the client
			send_http_error(conn, 400, "Bad Request",
					"Cannot parse HTTP request: [%.*s]",
					conn->data_len, conn->buf);
		} else if (strcmp(ri->http_version, "1.0")
			   && strcmp(ri->http_version, "1.1")) {
			// Request seems valid, but HTTP version is strange
			send_http_error(conn, 505, "HTTP version not supported",
					"");
			log_access(conn);
		} else {
			// Request is valid, handle it
			cl = get_header(ri, "Content-Length");
			conn->content_len =
			    cl == NULL ? -1 : strtoll(cl, NULL, 10);
			conn->birth_time = time(NULL);
			handle_request(conn);
			log_access(conn);
			discard_current_request_from_buffer(conn);
		}
	} while (conn->ctx->stop_flag == 0 &&
			(keep_alive_enabled && should_keep_alive(conn)));
}

// Worker threads take accepted socket from the queue
static int consume_socket(struct mg_context *ctx, struct socket *sp)
{
	(void)pthread_mutex_lock(&ctx->mutex);
	DEBUG_TRACE(("going idle"));

	// If the queue is empty, wait. We're idle at this point.
	while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
		pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
	}

	// If we're stopping, sq_head may be equal to sq_tail.
	if (ctx->sq_head > ctx->sq_tail) {
		// Copy socket from the queue and increment tail
		*sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
		ctx->sq_tail++;
		DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));

		// Wrap pointers if needed
		while (ctx->sq_tail > (int)ARRAY_SIZE(ctx->queue)) {
			ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
			ctx->sq_head -= ARRAY_SIZE(ctx->queue);
		}
	}

	(void)pthread_cond_signal(&ctx->sq_empty);
	(void)pthread_mutex_unlock(&ctx->mutex);

	return !ctx->stop_flag;
}

static void worker_thread(struct mg_context *ctx)
{
	struct mg_connection *conn;
	int buf_size = atoi(ctx->config[MAX_REQUEST_SIZE]);

	conn = (struct mg_connection *)calloc(1, sizeof(*conn) + buf_size);
	conn->buf_size = buf_size;
	conn->buf = (char *)(conn + 1);
	assert(conn != NULL);

	// Call consume_socket() even when ctx->stop_flag > 0, to let it signal
	// sq_empty condvar to wake up the master waiting in produce_socket()
	while (consume_socket(ctx, &conn->client)) {
		conn->birth_time = time(NULL);
		conn->ctx = ctx;

		// Fill in IP, port info early so even if SSL setup below fails,
		// error handler would have the corresponding info.
		// Thanks to Johannes Winkelmann for the patch.
		// TODO(lsm): Fix IPv6 case
		conn->request_info.remote_port =
		    ntohs(conn->client.rsa.sin.sin_port);
		memcpy(&conn->request_info.remote_ip,
		       &conn->client.rsa.sin.sin_addr.s_addr, 4);
		conn->request_info.remote_ip =
		    ntohl(conn->request_info.remote_ip);

		process_new_connection(conn);
		close_connection(conn);
	}
	free(conn);

	// Signal master that we're done with connection and exiting
	(void)pthread_mutex_lock(&ctx->mutex);
	ctx->num_threads--;
	(void)pthread_cond_signal(&ctx->cond);
	assert(ctx->num_threads >= 0);
	(void)pthread_mutex_unlock(&ctx->mutex);

	DEBUG_TRACE(("exiting"));
}

// Master thread adds accepted socket to a queue
static void produce_socket(struct mg_context *ctx, const struct socket *sp)
{
	(void)pthread_mutex_lock(&ctx->mutex);

	// If the queue is full, wait
	while (ctx->stop_flag == 0 &&
	       ctx->sq_head - ctx->sq_tail >= (int)ARRAY_SIZE(ctx->queue)) {
		(void)pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
	}

	if (ctx->sq_head - ctx->sq_tail < (int)ARRAY_SIZE(ctx->queue)) {
		// Copy socket to the queue and increment head
		ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
		ctx->sq_head++;
		DEBUG_TRACE(("queued socket %d", sp->sock));
	}

	(void)pthread_cond_signal(&ctx->sq_full);
	(void)pthread_mutex_unlock(&ctx->mutex);
}

static void accept_new_connection(const struct socket *listener,
				  struct mg_context *ctx)
{
	struct socket accepted;
	char src_addr[20];
	socklen_t len;

	len = sizeof(accepted.rsa);
	accepted.lsa = listener->lsa;
	accepted.sock = accept(listener->sock, &accepted.rsa.sa, &len);
	if (accepted.sock != INVALID_SOCKET) {
		// Put accepted socket structure into the queue
		DEBUG_TRACE(("accepted socket %d", accepted.sock));
		produce_socket(ctx, &accepted);
	}
}

static void master_thread(struct mg_context *ctx)
{
	fd_set read_set;
	struct timeval tv;
	struct socket *sp;
	int max_fd;

	while (ctx->stop_flag == 0) {
		FD_ZERO(&read_set);
		max_fd = -1;

		// Add listening sockets to the read set
		for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
			add_to_set(sp->sock, &read_set, &max_fd);
		}

		tv.tv_sec = 0;
		tv.tv_usec = 200 * 1000;

		if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) {
#ifdef _WIN32
			// On windows, if read_set and write_set are empty,
			// select() returns "Invalid parameter" error
			// (at least on my Windows XP Pro). So in this case, we sleep here.
			sleep(1);
#endif				// _WIN32
		} else {
			for (sp = ctx->listening_sockets; sp != NULL;
			     sp = sp->next) {
				if (ctx->stop_flag == 0
				    && FD_ISSET(sp->sock, &read_set)) {
					accept_new_connection(sp, ctx);
				}
			}
		}
	}
	DEBUG_TRACE(("stopping workers"));

	// Stop signal received: somebody called mg_stop. Quit.
	close_all_listening_sockets(ctx);

	// Wakeup workers that are waiting for connections to handle.
	pthread_cond_broadcast(&ctx->sq_full);

	// Wait until all threads finish
	(void)pthread_mutex_lock(&ctx->mutex);
	while (ctx->num_threads > 0) {
		(void)pthread_cond_wait(&ctx->cond, &ctx->mutex);
	}
	(void)pthread_mutex_unlock(&ctx->mutex);

	// All threads exited, no sync is needed. Destroy mutex and condvars
	(void)pthread_mutex_destroy(&ctx->mutex);
	(void)pthread_cond_destroy(&ctx->cond);
	(void)pthread_cond_destroy(&ctx->sq_empty);
	(void)pthread_cond_destroy(&ctx->sq_full);

	// Signal mg_stop() that we're done
	ctx->stop_flag = 2;

	DEBUG_TRACE(("exiting"));
}

static void free_context(struct mg_context *ctx)
{
	int i;

	// Deallocate config parameters
	for (i = 0; i < NUM_OPTIONS; i++) {
		if (ctx->config[i] != NULL)
			free(ctx->config[i]);
	}

	// Deallocate context itself
	free(ctx);
}

void mg_stop(struct mg_context *ctx)
{
	ctx->stop_flag = 1;

	// Wait until mg_fini() stops
	while (ctx->stop_flag != 2) {
		(void)sleep(0);
	}
	free_context(ctx);

#if defined(_WIN32) && !defined(__SYMBIAN32__)
	(void)WSACleanup();
#endif				// _WIN32
}

struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
			    const char **options)
{
	struct mg_context *ctx;
	const char *name, *value, *default_value;
	int i;

#if defined(_WIN32) && !defined(__SYMBIAN32__)
	WSADATA data;
	WSAStartup(MAKEWORD(2, 2), &data);
#endif				// _WIN32

	// Allocate context and initialize reasonable general case defaults.
	// TODO(lsm): do proper error handling here.
	ctx = (struct mg_context *)calloc(1, sizeof(*ctx));
	ctx->user_callback = user_callback;
	ctx->user_data = user_data;

	while (options && (name = *options++) != NULL) {
		if ((i = get_option_index(name)) == -1) {
			cry(fc(ctx), "Invalid option: %s", name);
			free_context(ctx);
			return NULL;
		} else if ((value = *options++) == NULL) {
			cry(fc(ctx), "%s: option value cannot be NULL", name);
			free_context(ctx);
			return NULL;
		}
		ctx->config[i] = mg_strdup(value);
		DEBUG_TRACE(("[%s] -> [%s]", name, value));
	}

	// Set default value if needed
	for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {
		default_value =
		    config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];
		if (ctx->config[i] == NULL && default_value != NULL) {
			ctx->config[i] = mg_strdup(default_value);
			DEBUG_TRACE(("Setting default: [%s] -> [%s]",
				     config_options[i *
						    ENTRIES_PER_CONFIG_OPTION +
						    1], default_value));
		}
	}

	// NOTE(lsm): order is important here. SSL certificates must
	// be initialized before listening ports. UID must be set last.
	if (!set_ports_option(ctx)
#if !defined(_WIN32)
	    || !set_uid_option(ctx)
#endif
	    ) {
		free_context(ctx);
		return NULL;
	}

#if !defined(_WIN32) && !defined(__SYMBIAN32__)
	// Ignore SIGPIPE signal, so if browser cancels the request, it
	// won't kill the whole process.
	(void)signal(SIGPIPE, SIG_IGN);
	// Also ignoring SIGCHLD to let the OS to reap zombies properly.
	(void)signal(SIGCHLD, SIG_IGN);
#endif				// !_WIN32

	(void)pthread_mutex_init(&ctx->mutex, NULL);
	(void)pthread_cond_init(&ctx->cond, NULL);
	(void)pthread_cond_init(&ctx->sq_empty, NULL);
	(void)pthread_cond_init(&ctx->sq_full, NULL);

	// Start master (listening) thread
	start_thread(ctx, (mg_thread_func_t) master_thread, ctx);

	// Start worker threads
	for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
		if (start_thread(ctx, (mg_thread_func_t) worker_thread, ctx) !=
		    0) {
			cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
		} else {
			ctx->num_threads++;
		}
	}

	return ctx;
}
